from typing import Any, Dict, List, Text

from rasa_sdk import Action, Tracker
from rasa_sdk.events import SlotSet, ActionExecutionRejected
from rasa_sdk.executor import CollectingDispatcher

from uuid import uuid4

from slack_sdk.models.messages.message import message
from sqlalchemy.testing import in_

from .db import SessionLocal
from sqlalchemy.orm import joinedload
from sqlalchemy import and_, or_, func
from datetime import datetime, timedelta
from .db_table_class import OrderInfo, Postsale, OrderStatus, ReceiveInfo, Region

class AskOrderId(Action):
  """
  让用户选择一个订单，并设置槽中的order_id,返回当前用户的订单
  """

  def name(self) -> str:
    return "action_ask_order_id"

  def run(
    self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
  ) -> List[Dict[Text, Any]]:
    events = []

    # 1打开SessionLocal,这个是一个数据库连接池，用于管理数据库连接
    with SessionLocal() as session:
        #取出订单的详细信息，筛选函数
      order_infos = (
        session.query(OrderInfo)#订单信息表
        .join(OrderInfo.order_status_) # 关联订单状态表
        .options(joinedload(OrderInfo.order_detail)) # 预加载 order_detail，订单详细信息
        .filter(self.get_query_condition(tracker))
        .all()
      )
    order_nums = len(order_infos)
    #2根据返回的订单数量做不同的输出
    #2.1 只返回了一个订单的情况
    if order_nums == 1:
      # 如果只有一个订单，询问是否查询此订单
      order_info = order_infos[0]
      message = [
        "查找到一个订单",
        f"[{order_info.order_status}]**订单ID**：{order_info.order_id}",
      ]
        # 一个订单可能有多个商品，所以有多个订单详细
      for order_detail in order_info.order_detail:
        message.append(f"- {order_detail.sku_name} × {order_detail.sku_count}")
      dispatcher.utter_message(
        text="\n".join(message),
        buttons=[
          {
            "title": "确认",
            "payload": f"/SetSlots(order_id={order_info.order_id})", # /的作用是声明这是一个指令
          },
          {"title": "返回", "payload": "/SetSlots(order_id=false)"},
        ],
      )
    # 2.2 返回多个订单的情况
    elif order_nums > 1:
      # 如果有多个订单，用户选择其中一个
      buttons = [
        {
          "title": "\n".join(
            [
              f"[{order_info.order_status}]订单ID：{order_info.order_id}",
            ]
            + [
              f"- {order_detail.sku_name} × {order_detail.sku_count}"
              for order_detail in order_info.order_detail
            ]
          ),
          "payload": f"/SetSlots(order_id={order_info.order_id})",
        }
        for order_info in order_infos
      ]
      buttons.append({"title": "返回", "payload": "/SetSlots(order_id=false)"})
      dispatcher.utter_message(text="请选择订单", buttons=buttons)
    # 2.3没有订单的情况,使用events可以收集所有要处理的操作，等rasa运行完所有操作后，再处理这些事件
    else:
      # 没有订单
      dispatcher.utter_message(text="暂无订单")
      events.append(SlotSet("order_id", "false"))
      # 打断action_listen动作
      events.append(ActionExecutionRejected("action_listen"))
    return events

  # 定义筛选函数用于筛选不同状态的订单
  def get_query_condition(self, tracker: Tracker):
    """
    获取查询条件
    """
    user_id = tracker.get_slot("user_id")
    goto = tracker.get_slot("goto")
    match goto:
      case "action_ask_order_id_shipped":
        # 查询已发货的订单，赛选条件是表中的user_in=当前用户的id，和表中的发货状态为已发货
        return and_(
          OrderInfo.user_id == user_id,
          OrderInfo.order_status == "已发货",
        )

      case "action_ask_order_id_shipped_delivered":
          # 用于进行投诉物流，筛选的订单状态可以为“已签收”“已发货”
          return and_(
              OrderInfo.user_id == user_id,
              OrderInfo.order_status.in_(["已发货","已签收"])
          )

      case "action_ask_order_id_before_completed_3_days":
         # 查询进行中，或3日内已完成的订单
         return and_(
             OrderInfo.user_id == user_id,
             OrderInfo.order_status != "已取消",
             or_(
                OrderInfo.order_status != "已完成",
                OrderInfo.complete_time > datetime.now() - timedelta(days=3),
             ),
         )
      case "action_ask_order_id_before_modify_reciver_info":
          # 修改订单的收货信息，赛选出已发货之前的即可
          return and_(
              OrderInfo.user_id == user_id,
              OrderStatus.order_status != "已取消",
              OrderStatus.status_code<320,
          )

      case "action_ask_order_id_before_shipped":
         # 查询已发货之前状态的订单，用于取消订单
        return and_(
            OrderInfo.user_id == user_id,
            OrderInfo.order_status != "已取消",
            OrderStatus.status_code <= 310,
        )

      case "action_ask_order_id_after_delivered":
         # 查询已签收、售后中、已完成的订单
        return and_(
            OrderInfo.user_id == user_id,
            OrderInfo.order_status != "已取消",
            OrderStatus.status_code >= 330,
        )


      
class GetOrderDetail(Action):
    """
    获取订单详情
    """

    def name(self) -> str:
        return "action_get_order_detail"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        order_id = tracker.get_slot("order_id")
        # 查询订单
        with SessionLocal() as session:
            order_info = (
                session.query(OrderInfo)
                .options(joinedload(OrderInfo.order_detail))
                .options(joinedload(OrderInfo.logistics))
                .options(joinedload(OrderInfo.receive))
                .options(joinedload(OrderInfo.order_status_))
                .filter_by(order_id=order_id)
                .first()
            )
        # 拼接订单信息
        message = [f"- [{order_info.order_status}]**订单ID**：{order_info.order_id}"]
        for k, v in {
            "创建时间": order_info.create_time,
            "支付时间": order_info.payment_time,
            "签收时间": order_info.delivered_time,
            "完成时间": order_info.complete_time,
        }.items():
            if v:
                message.append(f"  - {k}：{v}")
        # 拼接订单明细信息
        message.append("- **订单明细**：")
        total_total_amount = 0.0
        total_discount_amount = 0.0
        total_final_amount = 0.0
        for order_detail in order_info.order_detail:
            message.append(
                f"  - {order_detail.sku_name} × {order_detail.sku_count} | \
                {order_detail.total_amount}-{order_detail.discount_amount}={order_detail.final_amount}"
            )
            total_total_amount += float(order_detail.total_amount)
            total_discount_amount += float(order_detail.discount_amount)
            total_final_amount += float(order_detail.final_amount)
        message.append(
            f"  - **合计**：{total_total_amount}-{total_discount_amount}={total_final_amount}"
        )
        # 拼接收货信息
        message.extend(
            [
                "- **收货信息**：",
                f"  - 收货人：{order_info.receive.receiver_name}",
                f"  - 联系电话：{order_info.receive.receiver_phone}",
                f"  - 收货地址：{order_info.receive.receive_province}\
                {order_info.receive.receive_city}\
                {order_info.receive.receive_district}\
                {order_info.receive.receive_street_address}",
            ]
        )
        # 拼接最近物流信息
        logistics = order_info.logistics
        if logistics:
            message.append("- **最近物流信息**：")
            message.append(f"  - {logistics[0].logistics_tracking.splitlines()[-1]}")
        dispatcher.utter_message(text="\n".join(message))
        if order_info.order_status_.status_code < 400:
            return []
        # 打印售后信息
        # 获取所有订单明细ID
        order_detail_ids = [
            order_detail.order_detail_id for order_detail in order_info.order_detail
        ]
        # 查询每个订单明细最新的售后信息
        with SessionLocal() as session:
            # 子查询：postsale 按 order_detail_id 分组，取最大的 postsale.create_time
            subquery = (
                session.query(
                    Postsale.order_detail_id,
                    func.max(Postsale.create_time).label("max_time"),
                )
                .filter(Postsale.order_detail_id.in_(order_detail_ids))
                .group_by(Postsale.order_detail_id)
                .subquery()
            )
            postsales = (
                session.query(Postsale)
                .join(
                    subquery,
                    (Postsale.order_detail_id == subquery.c.order_detail_id)
                    & (Postsale.create_time == subquery.c.max_time),
                )
                .options(joinedload(Postsale.order_detail))
                .options(joinedload(Postsale.logistics))
                .all()
            )
        if not postsales:
            return []
        # 拼接售后信息
        for postsale in postsales:
            message = [
                f"- [{postsale.postsale_status}]**售后ID**：{postsale.postsale_id}"
            ]
            message.append("- **订单明细**：")
            message.append(
                f"  -{postsale.order_detail.sku_name} × {postsale.order_detail.sku_count}"
            )
            message.append(f"- **退款金额**：{postsale.refund_amount}")
            # 获取最新的物流信息
            if postsale.logistics:
                postsale.logistics = sorted(
                    postsale.logistics, key=lambda x: x.create_time, reverse=True
                )
                message.append("- **最近物流信息**：")
                message.append(
                    f"  - {postsale.logistics[0].logistics_tracking.splitlines()[-1]}"
                )
            dispatcher.utter_message(text="\n".join(message))
        return []

class GetReceiverId(Action):
    """
    获取该用户已有的收货人信息，并提供添加增加收货人和取消
    """

    def name(self) -> str:
        return "action_ask_receiver_id"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        user_id = tracker.get_slot("user_id")
        order_id = tracker.get_slot("order_id")

        # 1获取存在数据库中的已存入的收货人信息
        with SessionLocal() as session:
            receivers = (
                # 返回的receivers为列表
                session.query(ReceiveInfo)
                .filter(ReceiveInfo.user_id == user_id).distinct()
                .all()
            )
            # 获取当前订单的收货人信息
            current_receiver=(session.query(OrderInfo
            ).options(joinedload(OrderInfo.receive)
            ).filter(OrderInfo.order_id==order_id).first())
        # 2将取得的收货人信息展示给用户，
        # 并添加增加收货人信息和取消按钮，设置receiver_id的值
        buttons = [
            {
                "title": f"收货人:{receiver.receiver_name} - 电话:{receiver.receiver_phone} - 地址:{receiver.receive_province}{receiver.receive_city}{receiver.receive_district}{receiver.receive_street_address}",
                "payload": f"/SetSlots(receiver_id={receiver.receive_id})",
            }
            for receiver in receivers
        ]

        buttons.append(
            {
                "title": "修改收货人信息",
                "payload": f"/SetSlots(receiver_id=modify)",
            }
        )
        buttons.append(
            {
                "title": "取消",
                "payload": f"/SetSlots(receiver_id=false)",
            }
        )
        dispatcher.utter_message(
            text="请选择已有的收货人信息或增加收货人信息",
            buttons=buttons,
        )
        return [
            SlotSet("receiver_id", current_receiver.receive.receive_id),
            SlotSet("receiver_name",current_receiver.receive.receiver_name),
            SlotSet("receiver_phone", current_receiver.receive.receiver_phone),
            SlotSet("receiver_province", current_receiver.receive.receive_province),
            SlotSet("receiver_city", current_receiver.receive.receive_city),
            SlotSet("receiver_district", current_receiver.receive.receive_district),
            SlotSet("receiver_street_address", current_receiver.receive.receive_street_address),

        ]

class GetReceiverProvince(Action):
    """
    获取收货人的省份信息
    """

    def name(self) -> str:
        return "action_ask_receiver_province"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
        ) -> List[Dict[Text, Any]]:
        # 1获取省份信息并列出按钮让收货人进行选择
        with SessionLocal() as session:
            provinces =session.query(Region.province).distinct().all()
            buttons = [
                {
                    "title": province.province,
                    "payload": f"/SetSlots(receiver_province={province.province})",
                }
                for province in provinces
            ]
        dispatcher.utter_message(
            text="请选择收货人的省份",
            buttons=buttons,
        )
        return []

class GetReceiverCity(Action):
    """
    获取数据库中城市的信息以按钮的形式返回给收货人供其选择
    """

    def name(self) -> str:
        return "action_ask_receiver_city"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        province = tracker.get_slot("receiver_province")
        with SessionLocal() as session:
            cities =session.query(Region.city).filter(Region.province==province).distinct().all()
        buttons = [
            {
                "title": city.city,
                "payload": f"/SetSlots(receiver_city={city.city})",
            }
            for city in cities
        ]
        dispatcher.utter_message(text="选择城市",buttons=buttons)
        return []

class GetReceiverDistrict(Action):
    """
    取出收货人的区县让用户进行选择
    """

    def name(self) -> str:
        return "action_ask_receiver_district"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        city=tracker.get_slot("receiver_city")
        with SessionLocal() as session:
            districts =session.query(Region.district).filter(Region.city==city).distinct().all()
        buttons=[
            {
                "title": district.district,
                "payload": f"/SetSlots(receiver_district={district.district})",
            }
            for district in districts
        ]
        dispatcher.utter_message(text="选择区县",buttons=buttons)
        return []

class SetReceiverInfo(Action):
    """
    让用户进行确认修改内容并呈现给用户看
    """

    def name(self) -> str:
        return "action_ask_set_receiver_info"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        # 从槽中取出所有的信息,根据receiver_id判断是修改还是增加
        print("执行action_ask_set_receiver_info_to_db")
        receiver_id = tracker.get_slot("receiver_id")
        order_id=tracker.get_slot("order_id")
        user_id=tracker.get_slot("user_id")
        set_receiver_info=tracker.get_slot("set_receiver_info")
        judge_receiver_info=None
        receiver_info= None
        order_info=None
        # 取出修改后的信息用来显示
        # 0.0若是内容修改过了则取出修改后的内容
        if receiver_id in ("modify", "modified"):
            receiver_name = tracker.get_slot("receiver_name")
            receiver_phone = tracker.get_slot("receiver_phone")
            receive_province = tracker.get_slot("receiver_province")
            receive_city = tracker.get_slot("receiver_city")
            receive_district = tracker.get_slot("receiver_district")
            receive_street_address = tracker.get_slot("receiver_street_address")
            set_receiver_info = tracker.get_slot("set_receiver_info")
            #用上面的字段创建一张收货信息表
            receiver_info = ReceiveInfo(
                receive_id="rec" + uuid4().hex[:16],
                receiver_name=receiver_name,
                receiver_phone=receiver_phone,
                receive_province=receive_province,
                receive_city=receive_city,
                receive_district=receive_district,
                receive_street_address=receive_street_address,
                user_id=user_id,
            )
        #0.1如果没有进行额外的修改，选择了原有的收货人信息，从数据库拿出原有的该订单的收货人信息
        else:
            with SessionLocal() as session:
                receiver_info = session.query(ReceiveInfo
                ).filter(ReceiveInfo.receive_id==receiver_id).first()
        # 1 根据槽set_receiver_info如果为true修改并存入数据库
        # 1.1在存入数据前先检查当前订单的收货人信息是否与要写入的相同
        if set_receiver_info:
            with SessionLocal() as session:
                order_info = session.query(OrderInfo).filter(OrderInfo.order_id == order_id).first()
                # 1.2如果是新添加的收货人，先检查该收货人是否已经在数据库中,如果不在就添加，并给出反馈
                if receiver_id in ("modify", "modified"):
                    #用于判断该收货人信息是否已经存在
                    print("判断该收货人是否已经存在")
                    judge_receiver_info = session.query(ReceiveInfo).filter(
                        ReceiveInfo.receiver_phone == receiver_info.receiver_phone,
                        ReceiveInfo.receiver_name == receiver_info.receiver_name,
                        ReceiveInfo.receive_province == receiver_info.receive_province,
                        ReceiveInfo.receive_city == receiver_info.receive_city,
                        ReceiveInfo.receive_district == receiver_info.receive_district,
                        ReceiveInfo.receive_street_address == receiver_info.receive_street_address,
                        ReceiveInfo.user_id == user_id,
                        ReceiveInfo.receive_id==receiver_info.receive_id
                    ).first()
                    if judge_receiver_info:
                        dispatcher.utter_message(text="该收货人信息已经存在,无需重复添加")
                        order_info.receive_id = judge_receiver_info.receive_id
                        print("检查想新添加的是否存在已有的收货人：")
                        print(judge_receiver_info)
                    else:
                        session.add(receiver_info)
                        session.flush()
                        order_info.receive_id = receiver_info.receive_id
                    # 1.3修改当前订单的收货人信息为新的收货人，并给出反馈
                    session.commit()
                else:
                    order_info.receive_id = receiver_info.receive_id
                    session.commit()
            dispatcher.utter_message(text="修改成功")
        # 2 根据槽set_receiver_info为false或者未进行设置
        # 未进行设置或者为false则展示当前订单收货人的信息给用户
        else :
            message = [
                f"- 收货人姓名: {receiver_info.receiver_name}",
                f"- 联系电话: {receiver_info.receiver_phone}",
                f"- 收货省: {receiver_info.receive_province}",
                f"- 收货市: {receiver_info.receive_city}",
                f"- 收货城区: {receiver_info.receive_district}",
                f"- 收货地址: {receiver_info.receive_street_address}"
            ]
            # 一次性发送所有内容
            dispatcher.utter_message(
                text="\n".join(message)
            )
            dispatcher.utter_message(text="是否确认修改",buttons = [
                {"title": "确认", "payload": "/SetSlots(set_receiver_info=true)"},
                {"title": "取消", "payload": "/SetSlots(set_receiver_info=false)"}
            ])

        return []
    
class CancelOrder(Action):
    """
    取消订单
    """

    def name(self) -> str:
        return "action_cancel_order"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[str, Any]
    ) -> List[Dict[Text, Any]]:
        order_id = tracker.get_slot("order_id")
        with SessionLocal() as session:
            order_info = session.query(OrderInfo).filter_by(order_id=order_id).first()
            old_order_status = order_info.order_status
            order_info.order_status = "已取消"
            order_info.complete_time = datetime.now()
            session.commit()
        message = "订单已取消"
        if old_order_status == "待发货":
            message += "，退款金额将在24小时内返还您的账户"
        dispatcher.utter_message(text=message)
        return []